﻿/*  
 * Helix Engine
 * http://helixengine.codeplex.com/
 * 
 * Copyright (c) 2010 - 2011 Joe Hegarty
 *  
 * This source code is subject to the terms of the Microsoft Public License (Ms-PL). 
 *  
 * Redistribution and use in source and binary forms, with or without modification, 
 * is permitted provided that redistributions of the source code retain the above 
 * copyright notices and this file header. 
 * 
 * For details please see http://www.opensource.org/licenses/ms-pl.html 
 *  
 * All other rights reserved. 
 *
 */  


using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Effects;

using HelixEngine.Maths;


namespace HelixEngine.Render
{
    /// <remarks>
    /// Represents a render scene
    /// </remarks>
    public sealed class SceneManager
    {
        WriteableBitmap _renderTarget;
        int _sceneDepth;
        List<QueuedRender> _queuedWrites;


        /// <summary>
        /// 
        /// </summary>
        public WriteableBitmap RenderTarget
        {
            get
            {
                return _renderTarget;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public int Depth
        {
            get
            {
                return _sceneDepth;
            }

            set
            {
                _sceneDepth = value;
            }
        }

        /// <summary>
        /// Constructor of the SceneManager, initialises the render target
        /// </summary>
        /// <param name="id">The unique id of the scene manager</param>
        /// <param name="name">The friendly name of the scene manager</param>
        /// <param name="pixelWidth">Render target width in pixels</param>
        /// <param name="pixelHeight">Render target heigth in pixels</param>
        /// <param name="sceneDepth">The depth to render this scene into the parent scene at</param>
        public SceneManager(uint id, string name, int pixelWidth, int pixelHeight, int sceneDepth = 0)
        {
            _renderTarget = new WriteableBitmap(pixelWidth, pixelHeight);
            _sceneDepth = sceneDepth;
            _queuedWrites = new List<QueuedRender>();
        }

        /// <summary>
        /// Constructor of the SceneManager, initialises the render target
        /// </summary>
        /// <param name="id">The unique id of the scene manager</param>
        /// <param name="name">The friendly name of the scene manager</param>
        /// <param name="scene">The scene source</param>
        /// <param name="sceneDepth">The depth to render this scene into the parent scene at</param>
        public SceneManager(uint id, string name, WriteableBitmap scene, int sceneDepth = 0)
        {
            _renderTarget = scene;
            _sceneDepth = sceneDepth;
            _queuedWrites = new List<QueuedRender>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="scene"></param>
        /// <param name="blendMode"></param>
        public void QueueWrite(SceneManager scene, WriteableBitmapExtensions.BlendMode blendMode)
        {
            QueueWrite(scene, blendMode, Vector2.Zero);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="scene"></param>
        /// <param name="blendMode"></param>
        /// <param name="destPosition"></param>
        public void QueueWrite(SceneManager scene, WriteableBitmapExtensions.BlendMode blendMode, Vector2 destPosition)
        {
            _queuedWrites.Add(new QueuedRender(scene.Depth, scene.RenderTarget, destPosition, blendMode));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="zIndex"></param>
        /// <param name="bitmap"></param>
        /// <param name="destPosition"></param>
        /// <param name="blendMode"></param>
        public void QueueWrite(int zIndex, WriteableBitmap bitmap, Vector2 destPosition, WriteableBitmapExtensions.BlendMode blendMode)
        {
            _queuedWrites.Add(new QueuedRender(zIndex, bitmap, destPosition, blendMode));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="zIndex"></param>
        /// <param name="bitmap"></param>
        /// <param name="blendMode"></param>
        public void QueueWrite(int zIndex, WriteableBitmap bitmap, WriteableBitmapExtensions.BlendMode blendMode)
        {
            _queuedWrites.Add(new QueuedRender(zIndex, bitmap, Vector2.Zero, blendMode));
        }

        /// <summary>
        /// Executes the queued writes
        /// </summary>
        public void ExecuteQueuedWrites()
        {
            // Z-sort
            _queuedWrites.Sort(new DepthSorter());

            Clear();

            foreach (var render in _queuedWrites)
            {
                ImmediateWrite(render.Position, render.WriteableBitmap, render.BlendMode);
            }

            Render();

            _queuedWrites.Clear();

        }

        /// <summary>
        /// 
        /// </summary>
        public void Render()
        {
            _renderTarget.Invalidate();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void ApplyShader<T>() where T : ShaderEffect, new()
        {
            Image image = new Image();
            image.Effect = new T(); 
            image.Source = _renderTarget; 
            _renderTarget.Render(image, null);
            _renderTarget.Invalidate(); 
            image.Source = null;
            image.Effect = null;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Clear()
        {
            Array.Clear(_renderTarget.Pixels, 0, _renderTarget.Pixels.Length);
        }

        void ImmediateWrite(Vector2 destPosition, WriteableBitmap source, WriteableBitmapExtensions.BlendMode blendMode)
        {
            Rect sourceRect = new Rect(0, 0, source.PixelWidth, source.PixelHeight);
            _renderTarget.Blit(destPosition.ToPoint(), source, sourceRect, Colors.White, blendMode);
        }




    }
}
